package control;

import java.io.EOFException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import model.UserInfo;
import model.UserInfoClearText;
import util.CrackerHelper;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
/**
 *
 * @author Ragnarokr
 */
public class Slave {

//    public final static String MASTER_IP = "10.54.255.65";
    public final static String MASTER_IP = "localhost";
    public final static int PORT = 42000;
// <editor-fold defaultstate="collapsed" desc="PROPERTIES">
    protected Socket socket;

    /**
     * Get the value of socket
     *
     * @return the value of socket
     */
    public Socket getSocket() {
        return socket;
    }

    /**
     * Set the value of socket
     *
     * @param socket new value of socket
     */
    public void setSocket(Socket socket) {
        this.socket = socket;
    }
    protected List<UserInfo> userInfo;

    /**
     * Get the value of userInfo
     *
     * @return the value of userInfo
     */
    public List<UserInfo> getUserInfo() {
        return userInfo;
    }

    /**
     * Set the value of userInfo
     *
     * @param userInfo new value of userInfo
     */
    public void setUserInfo(List<UserInfo> userInfo) {
        this.userInfo = userInfo;
    }
    protected MessageDigest messageDigest;

    /**
     * Get the value of messageDigest
     *
     * @return the value of messageDigest
     */
    public MessageDigest getMessageDigest() {
        return messageDigest;
    }

    /**
     * Set the value of messageDigest
     *
     * @param messageDigest new value of messageDigest
     */
    public void setMessageDigest(MessageDigest messageDigest) {
        this.messageDigest = messageDigest;
    }
    protected List<String> dPart;

    /**
     * Get the value of dPart
     *
     * @return the value of dPart
     */
    public List<String> getDPart() {
        return dPart;
    }

    /**
     * Set the value of dPart
     *
     * @param dPart new value of dPart
     */
    public void setDPart(List<String> dPart) {
        this.dPart = dPart;
    }// </editor-fold>

    /**
     * Initiates a connection to the master
     * @throws IOException
     */
    private void init() throws IOException {
        try {
            Socket s = new Socket(MASTER_IP, PORT);
            setSocket(s);
            setMessageDigest(MessageDigest.getInstance("SHA"));
            readUserInfoFromMaster();
            while (true) {
                startProcess();
            }
        } catch (SocketException ex) {
            System.out.println("connection closed - socket exception");
            getSocket().close();
            System.exit(0);
        } catch (EOFException ex) {
            System.out.println("connection closed - end of file");
            getSocket().close();
            System.exit(0);
        } catch (ClassNotFoundException ex) {
            System.out.println("class not found exception");
            Logger.getLogger(Slave.class.getName()).log(Level.SEVERE, null, ex);
        } catch (NoSuchAlgorithmException ex) {
            System.out.println("no such algorithm exception");
            Logger.getLogger(Slave.class.getName()).log(Level.SEVERE, null, ex);
        } catch (UnknownHostException ex) {
            System.out.println("unknown host exception");
            Logger.getLogger(Slave.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * Reads a dictionary part from the master and starts the cracking
     * @throws IOException
     * @throws ClassNotFoundException
     */
    private void startProcess() throws IOException, ClassNotFoundException {
        readDPartFromMaster();
        startCracking();
    }

    /**
     * Reads a dictionary part from the master
     * @throws IOException
     * @throws ClassNotFoundException
     */
    private void readDPartFromMaster() throws IOException, ClassNotFoundException {
        ObjectInputStream oin = new ObjectInputStream(getSocket().getInputStream());
        List<String> mDPart = null;
        Object o = oin.readObject();
        if (o instanceof List) {
            mDPart = (List<String>) o;
        }
        setDPart(mDPart);
        System.out.println("From \"" + mDPart.get(0) + "\" to \"" + mDPart.get(mDPart.size() - 1) + "\"");
    }

    /**
     * Reads the user info from the master
     * @throws IOException
     * @throws ClassNotFoundException
     */
    private void readUserInfoFromMaster() throws IOException, ClassNotFoundException {
        ObjectInputStream oin = new ObjectInputStream(getSocket().getInputStream());
        List<UserInfo> mUserInfo = null;
        Object o = oin.readObject();
        if (o instanceof List) {
            mUserInfo = (List<UserInfo>) o;
        }
        setUserInfo(mUserInfo);
        System.out.println(mUserInfo);
    }

    /**
     * Starts the cracking process
     * @throws IOException
     */
    private void startCracking() throws IOException {
        List<UserInfoClearText> results = new ArrayList<UserInfoClearText>();
        for (String word : getDPart()) {
            results.addAll(CrackerHelper.checkWordWithVariations(getMessageDigest(), word, getUserInfo()));
        }
        if (results.size() > 0) {
            System.out.println(results);
        } else {
            System.out.println("No matches");
        }
        sendResultToMaster(results);
    }

    /**
     * Sends the results to the master
     * @param results
     * @throws IOException
     */
    private void sendResultToMaster(List<UserInfoClearText> results) throws IOException {
        ObjectOutputStream oout = new ObjectOutputStream(getSocket().getOutputStream());
        oout.writeObject(results);
    }

    /**
     * Creates an instance of the class Slave
     */
    public Slave() {
        try {
            init();
            startCracking();
        } catch (IOException ex) {
            Logger.getLogger(Slave.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < Runtime.getRuntime().availableProcessors(); i++) {
            Slave s = new Slave();
        }
    }
}
